class MisMatchError(BaseException):
    pass


# Factory
class PartTool(object):
    @staticmethod
    def to(part):
        if isinstance(part, Part):
            return part
        elif isinstance(part, str):
            if len(part) == 0:
                return Null
            return Str(part)
        elif isinstance(part, list):
            return List(part)
        else:
            raise TypeError("")


class Part(object):
    """
        how to define a part with start idx given?
        1. specified the end char
        2. specified the start char and the end char
        3. specified the start char and the len

        basic:
            Str, Null, Var, Scope, End(LongVar)
        expand:
            Or, List, Loop
    """
    _SIMPLE_STR = True

    def __call__(self, x_list: list, i: int, *args, **kwargs):
        raise NotImplementedError()

    def __str__(self):
        return self.__repr__()

    def __add__(self, other):
        if isinstance(self, List):
            return List(self.parts + [other])
        else:
            return List([self, other])

    def __or__(self, other):
        return Or(self, PartTool.to(other))


class End(Part):
    """
        how to define a part with start idx given?
        1. specified the end char
    """
    def __init__(self, end, include_end=False):
        super().__init__()
        self.end = end
        self.include_end = include_end

    def __call__(self, x_list: list, i: int, *args, **kwargs):
        start_i = i
        for i in range(start_i, len(x_list)):
            if x_list[i] == self.end:
                if self.include_end:
                    return start_i, i + 1, None
                else:
                    return start_i, i, None
        raise MisMatchError("mismatch {} vs .".format(self.end))

    def __repr__(self):
        return "{}({})".format(self.__class__.__name__, self.end)


class StartEnd(End):
    def __init__(self, start, end, include_end=False):
        super(StartEnd, self).__init__(end, include_end)
        self.start = start

    def __call__(self, x_list: list, i: int, *args, **kwargs):
        if x_list[i] != self.start:
            raise MisMatchError('mismatch {} vs {}.'.format(self.start, x_list[i]))
        return super(StartEnd, self).__call__(x_list, i, * args, **kwargs)

    def __repr__(self):
        return "{}({})".format(self.__class__.__name__, self.end)


class StrLen(Part):
    def __init__(self, string, len):
        """
        :param string:
        :param len: -1 means to last one of input x_list
        """
        super().__init__()
        self.string = string
        self.len = len

    def __call__(self, x_list: list, i: int, * args, **kwargs):
        if not self.string: return i, i, None
        if x_list[i] != self.string or (i + self.len) > len(x_list):
            raise MisMatchError('mismatch {} vs {}.'.format(self.string, x_list[i]))
        if self.len == -1:
            return i, len(x_list), None
        else:
            return i, i + self.len, None

    def __repr__(self):
        return "{}({}, {})".format(self.__class__.__name__, self.string, self.len)


class Str(StrLen):
    def __init__(self, string):
        super(Str, self).__init__(string, 1)

    def __repr__(self):
        if Part._SIMPLE_STR:
            return '"{}"'.format(self.string)
        return "{}({})".format(self.__class__.__name__, self.string)


class NullStr(Str):
    def __init__(self):
        super().__init__("")

    def __repr__(self):
        return "Null"


Null = NullStr()


class Var(Part):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def __call__(self, x_list, i, *args, **kwargs):
        return i, i + 1, {self.name: x_list[i]}

    def __repr__(self):
        return "$({})".format(self.name)


class EndVar(End):
    def __init__(self, name, end, include_end=False):
        super(EndVar, self).__init__(end, include_end)
        self.name = name

    def __call__(self, x_list, i, *args, **kwargs):
        i, j, _ = super(EndVar, self).__call__(x_list, i, *args, **kwargs)
        return i, j, {self.name: x_list[i:j]}


def valid_bracket(record, k, left_item='<', right_item='>'):
    """
    找到与左尖括号匹配的右尖括号，k表示左尖括号出现的index
    """
    bracket = 1
    for i in range(k, len(record)):
        token = record[i]
        if token == left_item:
            bracket += 1
        elif token == right_item:
            bracket -= 1
        if bracket == 0:
            return i
    return None


class Scope(Part):
    def __init__(self, left, right):
        super().__init__()
        self.left, self.right = left, right

    def __call__(self, x_list, i, *args, **kwargs):
        if x_list[i] == self.left:
            left_i = i
        else:
            raise MisMatchError("mismatch {} vs .".format(self.left))

        right_i = valid_bracket(x_list, left_i + 1, self.left, self.right)
        if right_i is None:
            raise MisMatchError("mismatch {} vs .".format(self.right))

        return left_i, right_i + 1, None

    def __repr__(self):
        if Part._SIMPLE_STR:
            return "{}...{}".format(self.left, self.right)
        return "{}({}...{})".format(self.__class__.__name__, self.left, self.right)


class CombinePart(Part):
    def __call__(self, x_list: list, i: int, *args, **kwargs):
        raise NotImplementedError()


class Or(CombinePart):
    def __init__(self, *parts):
        super().__init__()
        self.parts = [PartTool.to(sep) for sep in parts]

    def __call__(self, x_list, i, *args, **kwrag):
        for sep in self.parts:
            try:
                return sep(x_list, i)
            except MisMatchError as e:
                pass
        raise MisMatchError("mismatch in {} vs {}.".format(self, x_list[i:]))

    def __repr__(self):
        if Part._SIMPLE_STR:
            return "({})".format(' | '.join([str(p) for p in self.parts]))
        return "{}{}".format(self.__class__.__name__, self.parts)


class List(CombinePart):
    def __init__(self, parts, ignore_left_parts=False):
        super(List, self).__init__()
        self.parts = [PartTool.to(sep) for sep in parts]
        self.ignore_left_parts = ignore_left_parts

    def __call__(self, x_list, i=0, *args, **kwargs):
        start_i = i

        results = [None] * len(self.parts)
        for sep_i, sep in enumerate(self.parts):
            if isinstance(sep, Part):
                results[sep_i] = j, i, _ = sep(x_list, i)
            else:
                raise ValueError("must be Part")

            if i >= len(x_list): break
        if (not self.ignore_left_parts) and sep_i + 1 != len(self.parts):
            raise MisMatchError("mismatch.")
        return start_i, i, results

    def __repr__(self):
        if Part._SIMPLE_STR:
            return '(' + ' + '.join([str(p) for p in self.parts]) + ')'
        return self.parts.__repr__()

    def __iadd__(self, other):
        self.parts.append(other)


class Repeat(CombinePart):
    def __init__(self, part):
        super().__init__()
        self.part = PartTool.to(part)

    def __call__(self, x_list, i, *args, **kwargs):
        start_i = i
        results = []
        while True:
            try:
                res = self.part(x_list, i, *args, **kwargs)
                results.append(res)
                i = res[1]
            except MisMatchError as e:
                break
        return start_i, i, results

    def __repr__(self):
        if Part._SIMPLE_STR:
            return '{}*'.format(self.part)
        return "{}({})".format(self.__class__.__name__, self.part)


if __name__ == "__main__":
    # test 1
    token_list = ['class', 'A', ':', 'B', ';']
    separator = List(["class", Var("classname"), ":", Var("superclass"), ';'])
    print(separator(token_list))

    # test2
    token_lists = [['class', 'A', ':', 'B', '{', '...', '}', ';'], ['class', 'A', ':', 'B', ';'], ['class', 'A', ';']]
    separator = List(["class", Var("classname"), Or([":", Var("superclass")], Null), Or(Scope('{', '}'), ';')])
    print(separator)

    for token_list in token_lists:
        print(separator(token_list))

    # test3
    token_lists = [['class', 'A', ';'], ['class', 'A', ':', 'B', ';'], ['class', 'A', ':', 'B', ',', 'C', ';']]
    separator = List(["class", Var("classname"),
                      Or([":", Var("superclass"), Repeat([',', Var('superclass')])], Null),
                      Or(Scope('<', '>'), ';')])
    print(separator)

    for token_list in token_lists:
        print(separator(token_list))
